package com.zzw.bridge.business.service.impl;

import com.zzw.bridge.business.dto.FileDownloadDTO;
import com.zzw.bridge.business.service.FileService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;

/**
 * 类描述
 *
 * @author XiaoJie
 * @date 2025/5/22 14:08
 */
@Service
public class FileServiceImpl implements FileService {
    @Value("${file.temp-dir:D:/UserHome/testzip}")
    private String tempDir;

    @Override
    public void downloadFile(FileDownloadDTO dto, HttpServletResponse response) {
        // 1. 参数校验
        String filePath = dto.getFilePath();
        String fileName = dto.getFileName();
        if (filePath == null || fileName == null) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        try {
            // 2. 路径安全校验（防御路径遍历攻击）
            Path resolvedPath = Paths.get(filePath, fileName).normalize();
            // 3. 文件存在性及可读性检查
            File file = resolvedPath.toFile();
            BasicFileAttributes basicFileAttributes = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
            if (!file.exists() || !basicFileAttributes.isRegularFile()) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            if (!file.canRead()) {
                response.sendError(HttpServletResponse.SC_FORBIDDEN, "文件不可读");
                return;
            }

            //设置响应头
            setResponseHead(response, fileName, basicFileAttributes);

            // 5. 流式传输文件内容（支持大文件）
            try (InputStream is = Files.newInputStream(file.toPath());
                 OutputStream os = response.getOutputStream()) {

                byte[] buffer = new byte[4096]; // 4KB缓冲区
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                    os.flush(); // 确保数据及时发送
                }
            } catch (IOException e) {
                // 处理客户端中断下载的情况
                if (!response.isCommitted()) {
                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                }
            }

        } catch (IOException e) {
            if (!response.isCommitted()) {
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            }
        }
    }

    @Override
    public ResponseEntity<Resource> downloadFileResponseEntity(FileDownloadDTO dto) throws UnsupportedEncodingException {
        String filePath = tempDir.concat("/"+dto.getFilePath());

        // 构建文件路径
        File file = new File(filePath,dto.getFileName());

        // 检查文件是否存在
        if (!file.exists()) {
            return ResponseEntity.notFound().build();
        }

        // 创建资源对象
        Resource resource = new FileSystemResource(file);

        // 设置响应头
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=" + URLEncoder.encode(dto.getFileName(), StandardCharsets.UTF_8.toString()));
        headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
        headers.add(HttpHeaders.PRAGMA, "no-cache");
        headers.add(HttpHeaders.EXPIRES, "0");
        headers.add(HttpHeaders.CONTENT_TYPE, "application/octet-stream");

        // 返回响应实体
        return ResponseEntity.ok()
                .headers(headers)
                .contentLength(file.length())
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(resource);
    }

    /**
     * 设置响应头
     *
     * @param response "条件"
     * @param fileName "条件"
     * @param basicFileAttributes "条件"
     */
    private void setResponseHead(HttpServletResponse response, String fileName, BasicFileAttributes basicFileAttributes) {
        // 4. 设置响应头
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader("Content-Disposition",
                "attachment; filename=\"" + fileName + "\"");
        response.setContentLengthLong(basicFileAttributes.size());
    }
}
